package com.aim.analysis.excel;

import com.aim.analysis.artifact.ArtifactSubFolder;
import com.aim.analysis.artifact.Strategy;
import com.aim.analysis.questionnaire.QValueExtractor;
import com.aim.analysis.records.*;
import com.aim.util.ListUtil;
import com.aim.util.MeanVarPair;
import com.aim.util.Pair;
import com.google.common.primitives.Doubles;
import org.apache.commons.math3.stat.inference.TTest;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.text.SimpleDateFormat;
import java.util.*;

/**
 * User: Avraham Shvartzon
 * Date: 7/28/14
 */
public class AlertNoAlertHitReport extends HitReport {

    private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public AlertNoAlertHitReport(String fileSuffix) {
        super(fileSuffix);
    }

    @Override
    public ArtifactSubFolder getArtifactSubFolder() {
        return ArtifactSubFolder.RECORD_PER_HIT;
    }

    @Override
    public boolean execute(Collection<Hit> hitCollection) throws Exception {

        XSSFWorkbook workbook = new XSSFWorkbook();
        writeGameDetails(workbook,  hitCollection);
        writeStatistic(workbook, hitCollection);

        return writeToFile(workbook);
    }



    protected void writeStatistic(XSSFWorkbook workbook, Collection<Hit> hitCollection) {
        final HitValueExtractor<Pair<Game,Game>> gameSwaper = new HitValueExtractor<Pair<Game, Game>>("") {
            @Override
            public Pair<Game, Game> getValue(Hit hit) {
                List<Game> orderedGames = orderingGames(hit.gameList);
                Game g2 = orderedGames.get(1);
                Game g3 = orderedGames.get(2);
                return new Pair<Game, Game>(g2,g3);
            }
        };

        final HitValueExtractor<Pair<Double, Double>> scoreExtractor = new HitValueExtractor<Pair<Double, Double>>("") {

            @Override
            public Pair<Double, Double> getValue(Hit hit) {
                Pair<Game, Game> games = gameSwaper.getValue(hit);
                return new Pair<Double, Double>((double)games.a.getCalculatedScore(), (double)games.b.getCalculatedScore());
            }
        };

        XSSFSheet sheet = workbook.createSheet("statistic");
        TTestHitCalculator all = new TTestHitCalculator("not filter", scoreExtractor);
        TTestHitCalculator netScoreIncrement = new TTestHitCalculator("filtered by netScoreIncrement < 0.5", scoreExtractor);
        TTestHitCalculator netScoreIncrementEqualConditions = new TTestHitCalculator("filtered by netScoreIncrement < 0.5\n(equalConditions)", scoreExtractor);
        TTestHitCalculator takeMaintenance = new TTestHitCalculator("filtered by take maintenance", scoreExtractor);

        int alertFirst = 0;
        int alertLast = 0;

        for (Hit hit : hitCollection) {
            if (HitValueExtractor.gameWithAlertFirst.getValue(hit)){
                alertFirst++;
            } else {
                alertLast++;
            }

            all.add(hit);
            if (HitValueExtractor.netScoreIncrement.getValue(hit) < 0.5){
                netScoreIncrement.add(hit);
            }

            Integer mnt2 = GameValueExtractor.maintenance.getValue(hit.gameList.get(1));
            Integer mnt3 = GameValueExtractor.maintenance.getValue(hit.gameList.get(2));
            if (mnt2> 0 && mnt3 > 0){
                takeMaintenance.add(hit);
            }
        }

        int numForEachCondition = Math.min(alertFirst, alertLast);
        alertFirst = 0;
        alertLast = 0;
        for (Hit hit : hitCollection) {
            if (HitValueExtractor.netScoreIncrement.getValue(hit) >= 0.5){
                continue;
            }

            if (HitValueExtractor.gameWithAlertFirst.getValue(hit)){
                if (alertFirst <= numForEachCondition){
                    netScoreIncrementEqualConditions.add(hit);
                    alertFirst++;
                }
            } else {
                if (alertLast <= numForEachCondition){
                    netScoreIncrementEqualConditions.add(hit);
                    alertLast++;
                }
            }
        }

        int rowIndex = 0;
        writeTTestHeader(sheet.createRow(rowIndex++));
        writeTTest(sheet.createRow(rowIndex++), all);
        writeTTest(sheet.createRow(rowIndex++), netScoreIncrement);
        writeTTest(sheet.createRow(rowIndex++), netScoreIncrementEqualConditions);
        writeTTest(sheet.createRow(rowIndex++), takeMaintenance);

        List<Integer> responseDelays = new ArrayList<Integer>();
        for (Hit hit : hitCollection) {
            Pair<Game, Game> games = gameSwaper.getValue(hit);
            if (games.a.responseDelayData != null){
                responseDelays.addAll(games.a.responseDelayData);
            }
        }

        MeanVarPair meanVarPair = ListUtil.meanAndVariance(responseDelays);
        double median = ListUtil.median(responseDelays);
        System.out.println("responseDelays: " + meanVarPair.mean() + ", " + median  + ", " + meanVarPair.variance() + "," + responseDelays.size());
    }

    private void writeTTestHeader(XSSFRow header) {
        header.createCell(0).setCellValue("Filter");
        header.createCell(1).setCellValue("P-Value");
        header.createCell(2).setCellValue("T Stat");
        header.createCell(3).setCellValue("N");

    }

    private void writeTTest(XSSFRow row, TTestHitCalculator calculator){
        calculator.calc();
        row.createCell(0).setCellValue(calculator.name);
        row.createCell(1).setCellValue(calculator.getPairedTTest());
        row.createCell(2).setCellValue(calculator.getPairedT());
        row.createCell(3).setCellValue(calculator.numOfSamples());
    }

    protected void writeGameDetails(XSSFWorkbook workbook, Collection<Hit> hitCollection) {
        XSSFSheet sheet = workbook.createSheet("Games details");
        List<GameValueExtractor> gameExtractors = getGameExtractors();
        List<QValueExtractor> questionnaireExtractors = getQuestionnaireExtractors();
        List<HitValueExtractor<Double>> hitExtractors = getHitExtractor();

        //1. write header
        XSSFRow header = sheet.createRow(0);

        int cellIndex = 0;
        header.createCell(cellIndex++).setCellValue("date");
        header.createCell(cellIndex++).setCellValue("workerId");
        header.createCell(cellIndex++).setCellValue("assignmentId");
        header.createCell(cellIndex++).setCellValue("score g1");

        // game columns header
        for (int i = 0; i < 2; i++) {
            String suffix = getGameSuffix(i);
            for (GameValueExtractor gameValueExtractor : gameExtractors) {
                header.createCell(cellIndex++).setCellValue(gameValueExtractor.name + suffix);
            }
        }

        for (HitValueExtractor<Double> hitExtractor : hitExtractors) {
            header.createCell(cellIndex++).setCellValue(hitExtractor.name);
        }


        // demographic & behaviour
        for (QValueExtractor questionnaireExtractor : questionnaireExtractors) {
            header.createCell(cellIndex++).setCellValue(questionnaireExtractor.name);
        }


        //2. write content
        int rowIndex = 1;
        for (Hit hit : hitCollection) {
            XSSFRow row = sheet.createRow(rowIndex++);
            cellIndex = 0;

            // write hit data
            row.createCell(cellIndex++).setCellValue(hit.getDate().getTime());
            row.createCell(cellIndex++).setCellValue(hit.workerId);
            row.createCell(cellIndex++).setCellValue(hit.assignmentId);
            row.createCell(cellIndex++).setCellValue(hit.gameList.get(0).getCalculatedScore());

            // write games (2-3) data
//
            List<Game> orderedGames = orderingGames(hit.gameList);

            for (int i = 1; i <= 2; i++) {
                Game game = orderedGames.get(i);
                for (GameValueExtractor gameExtractor : gameExtractors) {
                    writeValue(cellIndex, row, game, gameExtractor, workbook);
                    cellIndex++;
                }
            }

            for (HitValueExtractor<Double> hitExtractor : hitExtractors) {
                XSSFCell cell = row.createCell(cellIndex++);
                writeValue(cell, hitExtractor, hit);
            }

            for (QValueExtractor questionnaireExtractor : questionnaireExtractors) {
                Object value = questionnaireExtractor.getValue(hit.questionnaireRecord);
                row.createCell(cellIndex++).setCellValue(value != null ? value.toString() : "N/A");
            }
        }
    }

    @Override
    protected int getNumOfGames() {
        return 2;
    }

    protected String getGameSuffix(int i) {
        return i == 0 ? "G_A" : "G_WO_A";
    }


    protected List<Game> orderingGames(List<Game> unorderedGames){
        List<Game> orderedGames = new ArrayList<Game>(unorderedGames);
        GameKey key = orderedGames.get(2).getKey();
        //if (key.gameDescription.equals("alert3SecBefore")) {
        if (key.strategy == Strategy.ALERT_3_SEC){
        //if (orderedGames.get(1).getKey().gameNumber == 3) {
             Collections.swap(orderedGames, 1, 2);
        }
        return orderedGames;
    }

    private static class TTestHitCalculator{

        Double pairedTTest = null;
        Double pairedT = null;
        String name;

        List<Double> d1 = new ArrayList<Double>();
        List<Double> d2 = new ArrayList<Double>();
        HitValueExtractor<Pair<Double,Double>> extractor;

        public TTestHitCalculator(String name, HitValueExtractor<Pair<Double,Double>> extractor) {
            this.name = name;
            this.extractor = extractor;
        }

        public void add(Hit hit){
            Pair<Double, Double> pair = extractor.getValue(hit);
            d1.add(pair.a);
            d2.add(pair.b);
        }

        private Float getPairedTTest() {
            return nanIfNull(pairedTTest);
        }

        private Float getPairedT() {
            return nanIfNull(pairedT);
        }

        private Float nanIfNull(Double value){
            return value == null ? Float.NaN : value.floatValue();
        }

        public void calc(){
            TTest tTest = new TTest();
            if (d1.isEmpty() || d1.size() < 2){
                return ;
            }
            double[] d1Arr = Doubles.toArray(d1);
            double[] d2Arr = Doubles.toArray(d2);

            this.pairedTTest = tTest.pairedTTest(d1Arr,d2Arr)/2;
            this.pairedT = tTest.pairedT(d1Arr, d2Arr);

            System.out.println("pairedTTest\t" + tTest.pairedTTest(Doubles.toArray(d1), Doubles.toArray(d2)));
            System.out.println("pairedT: " + tTest.pairedT(Doubles.toArray(d1), Doubles.toArray(d2)));
            System.out.println("tTest\t" + tTest.tTest(Doubles.toArray(d1), Doubles.toArray(d2)));
            System.out.println("t: " + tTest.t(Doubles.toArray(d1), Doubles.toArray(d2)));

           // return tTest.pairedTTest(Doubles.toArray(d1), Doubles.toArray(d2))/2;
        }

        public int numOfSamples() {
            return d1.size();
        }
    }
}
